Repository navigation
feat(ui): announce blocking actions in the header and add a timing page with Buckaroo's spans - #318
Merged
Merged
Conversation
…iming page The companion announces each blocking action before it runs and again when it ends, the header shows the running action and the last one, and GET /api/timing returns the companion's actions beside the spans Buckaroo posted back. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eue and for trace labels Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…a timing page activity.ts holds the pure state: a reducer for action_start and action_end, the now and last labels, the wait a load_expr POST spent in Buckaroo's queue, and the timeline layout. SSEContext keeps it without bumping version; ActivityStrip shows it in the header; TimingPage lays the companion's actions and Buckaroo's spans on one axis. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…burst of activity The rows lacked the flex layout the shared waterfall classes give, and one axis for the whole window made a half-second action a sliver next to another half a minute later. Rows within 2 s of the latest end share an axis. The note on a slow load_expr now says when Buckaroo began handling it, since the gap also holds the request's transfer and parsing, not only queueing. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…em over SSE, and serve /api/timing
tallyman_core/activity.py tracks running and finished actions and tells listeners at each start and end. load_session announces preparing the entry and the load_expr POST; the diff, promote, save-code, reset, staleness and recalc routes announce themselves. The companion forwards the events over SSE and GET /{project}/api/timing returns the actions beside the spans Buckaroo posted back.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…trip and timing page Python: a malformed telemetry span 500s /api/timing; reading spans parses the whole file; refused requests (read-only, unknown project) are announced as failed actions; a forced reload, the klass reload and the diff view's load_expr are not announced; the async routes hold the event loop through their blocking work, so action_start is held until the work is done; the hello event carries no activity snapshot. Vitest: formatDuration prints the wrong unit at its edges; a zero-length row sticks out of its track; seeding the header from a snapshot, the project filter, span keys and alias labels do not exist yet. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…d fix the timing page's labels The header's state is replaced by the activity snapshot on each SSE hello (first connect and every reconnect), so an action_end missed while disconnected no longer leaves a 'now:' running for good, and /api/timing is no longer read just to seed it. Activity is reset when the project changes and live events are filtered to the open project. Activity moves into its own context so an action event re-renders the strip and the timing page, not every useSSE() consumer. formatDuration picks its unit from the rounded value. layoutTimeline keeps a minimum-width row at the end inside the track, and the grid-load waterfall uses it instead of its own copy of the math. Timing rows are keyed by what the span is rather than its index, and aliases are labelled with their version and refetched when entries change. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…pshot on hello, and read telemetry cheaply The async routes ran their blocking steps on the event loop, so the action_start for a slow promote or save was held until the work was done. promote_diff and put_code now run diff_keys, cached_result_expr and build_and_persist on the thread pool, and reset, recalc, auto-recalc and /internal/notify do the same for reload_project_sessions. The SSE hello event carries the project's running and recent actions, which the SPA replaces its header state with. A request for an unknown project, or a write in serve mode, is refused before it is announced. A forced reload, the klass reload and the diff view's load_expr are announced like the other POSTs that queue behind Buckaroo's stats. /api/timing skips span records whose times are not finite numbers instead of failing, and reads telemetry.jsonl backward only as far as its window instead of parsing the whole file on every poll. The test that added a listener to the process-wide tracker now removes it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contributor
Author
Review of #318 and how each finding was addressedThe review found 13 issues. The failing tests are in 49d16bc. The frontend fixes are in c85b787 and the backend fixes in 7da3036. The test commit was seen failing on CI in two runs: vitest first (14 failures), then pytest once the frontend commit let that step run (12 failures). The last run is green. Only the
Not covered by a dedicated test: the |
… of a wrapping decorator The decorator had to return an async wrapper for async routes and a sync one for sync routes, and copy the signature with functools.wraps. A yield dependency does the same job without wrapping the route: FastAPI runs it on a worker thread, and the route body runs between the yield and the end of the with. One difference: a request whose body fails validation (422) is now announced and recorded as an error, because FastAPI validates the body after it has entered the dependency. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a page waits, nothing says what it is waiting on. On 2026-10-08 loading
carstook 3.27 s: the companion's POST to Buckaroo sat for 3.2 s behind a 4.5 s summary-stats run forcamera_violations(Buckaroo runs everything on one IOLoop thread), then Buckaroo's own handler took 39 ms. The activity log has the 3.27 s, andtelemetry.jsonlhas both the stats run and the 39 ms, but nothing puts them side by side.Fix
tallyman_core/activity.py: a process-wide tracker.track(name, detail)tells listeners the action is about to start (action_start) and again when it ends (action_end, with the duration); an exception is recorded as the action's error and re-raised. The companion forwards both over the existing SSE channel (from worker threads, through the loop).prepare entryand thebuckaroo load_exprPOST inBuckarooManager.load_session, and the diff data, promote diff, save code, reset, staleness and recalc routes.now: <action> <detail> · <elapsed>while one runs (the longest,+N morefor the rest),last: <action> <detail> · <time>after, linking to the timing page. SSE action events do not bumpversion, so they do not refetch the catalog.GET /{project}/api/timing?window_s=: the companion's actions and the spans Buckaroo posted back totelemetry.jsonl, on one clock (epoch ms).load_exprPOST says how long after it started Buckaroo began handling it. That gap is queueing plus the request's transfer and parsing; the page does not separate them.Not in this PR: Buckaroo announcing the start of its own work (it posts a span only when it ends, so the header cannot say a stats run is about to start; that needs a change in the buckaroo repo), and actions that run in the MCP process. The log is in memory, so a restart empties it.
Checked in a browser against a running companion: the pill showed
now: buckaroo load_expr 71d4f4fc7932 · 3 msand thenlast: … · 543 ms, and the timing page showed the companion's POST, Buckaroo'sload_expr,ws_first_payloadandstats.completefor it.🤖 Generated with Claude Code